/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * TrainingBook
 * Copyright (C) Arnaud Duval 2010 <arnaud.duval@gmail.com>
 * 
 * TrainingBook is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * TrainingBook is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "x-y-plot-area.hpp"
#include <iostream>
#include <assert.h>

///@brief Constructor
///@warning Limited to 3 plots for the moment
///@warning Number of plots should be dynamic
XYPlotArea::XYPlotArea()
{
    nbPlot = 2;
    yList = new std::list<double>[nbPlot];
    y_min = new double[nbPlot];
    y_max = new double[nbPlot];
    
    colors = new RGBcolor[3];
    
    colors[0].SetPredefined(RED_COLOR);
    colors[1].SetPredefined(GREEN_COLOR);
    colors[2].SetPredefined(BLUE_COLOR);

    idx_to_fill = 0;
}

///@brief Destructor
XYPlotArea::~XYPlotArea()
{
	if(nbPlot != 0)
	{
		delete[] yList;
		delete[] y_min;
		delete[] y_max;
	}
	delete[] colors;
}

///@brief Set the x list
///@param x List of x values
void XYPlotArea::SetXList(std::list<double> x)
{
	xList.clear();
	xList = x;
}

///@brief Set the y list
///@param y List of y values
///@param idx Index of the plot for wich the Y data should be set (begin at 0)
void XYPlotArea::SetYList(std::list<double> y, unsigned int idx)
{
	assert(idx < nbPlot);
	yList[idx].clear();
	yList[idx] = y;
}

///@brief Override default signal handler
bool XYPlotArea::on_expose_event(GdkEventExpose* event)
{
    Glib::RefPtr<Gdk::Window> window = get_window();
    if(window)
	{
		Gtk::Allocation allocation = get_allocation();
		const int width = allocation.get_width();
		const int height = allocation.get_height();
		
		
		Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
		//Clip to the area indicated by the expose event so that we only redraw
		//the portion of the window that need to be redrawn
		cr->rectangle(event->area.x, event->area.y, event->area.width, event->area.height);
		cr->clip();
		cr->set_source_rgb(0., 0., 0.);
		
		//scale to unit square(0 to 1 width and height)
		cr->scale(width, height);
		
		DrawAxis(cr);
		
		
		//draw curve
		
		//Coefficients to scale the values
		double a1 = 0.9/(x_max - x_min);
		double b1 = (0.05*x_max - 0.95*x_min)/(x_max - x_min);
		for(unsigned int i = 0 ; i < nbPlot ; i++)
		{
			cr->set_line_width(0.001);
			cr->set_source_rgb(colors[i].Red(), colors[i].Green(), colors[i].Blue());
			if(xList.size() != yList[i].size())
			{
				std::cerr << "Error in data to plot : not the same number of values for X and Y\n";
				std::cerr << "X : " << xList.size() << " values\n";
				std::cerr << "Y : " << yList[i].size() << " values\n";		
				return false;
			}
		
			std::list<double>::iterator litx(xList.begin()), lendx(xList.end());
			std::list<double>::iterator lity(yList[i].begin());
			//Coefficients to scale the values
			double a2 = -0.9/(y_max[i] - y_min[i]);
			double b2 = (0.95*y_max[i] - 0.05*y_min[i])/(y_max[i] - y_min[i]);
			for(;litx != lendx ; ++litx)
			{
				if(litx == xList.begin())
					cr->move_to(a1*(*litx) + b1, a2*(*lity) + b2);
				else
					cr->line_to(a1*(*litx) + b1, a2*(*lity) + b2);
				++lity;
			}
		
			cr->stroke();
		}
	
		///Tracé avec des trapezes
		//cr->set_line_width(0.001);
		
		if(xList.size() != yList[idx_to_fill].size())
		{
			std::cerr << "Error in data to plot : not the same number of values for X and Y\n";
			std::cerr << "X : " << xList.size() << " values\n";
			std::cerr << "Y : " << yList[idx_to_fill].size() << " values\n";
			return false;
		}
		
		std::list<double>::iterator litx(xList.begin()), lendx(xList.end());
		std::list<double>::iterator lity(yList[idx_to_fill].begin());
		//Coefficients to scale the values
		double a2 = -0.9/(y_max[idx_to_fill] - y_min[idx_to_fill]);
		double b2 = (0.95*y_max[idx_to_fill] - 0.05*y_min[idx_to_fill])/(y_max[idx_to_fill] - y_min[idx_to_fill]);
		double lastXvalue;
		double lastYvalue;
		for(;litx != lendx ; ++litx)
		{
			if(litx == xList.begin())
			{
				lastXvalue = *litx;
				lastYvalue = *lity;
			}
			else
			{
				double slope = ((*lity) - lastYvalue) / (1000.*((*litx) - lastXvalue));
				RGBcolor fillcolor;
				if((slope >= 0.05) && (slope < 0.07))
					fillcolor.SetPredefined(YELLOW_COLOR);
				else if((slope >= 0.07)&&(slope < 0.1))
					fillcolor.SetPredefined(ORANGE_COLOR);
				else if((slope >= 0.1)&&(slope < 0.15))
					fillcolor.SetPredefined(BROWN_COLOR);
				else if((slope >= 0.15))
					fillcolor.SetPredefined(RED_COLOR);
				
				else
					fillcolor.SetPredefined(WHITE_COLOR);

				cr->set_source_rgb(fillcolor.Red(), fillcolor.Green(), fillcolor.Blue());
				cr->move_to(a1*(lastXvalue) + b1, a2*y_min[idx_to_fill] + b2);
				cr->line_to(a1*(lastXvalue) + b1, a2*(lastYvalue) + b2);
				cr->line_to(a1*(*litx) + b1, a2*(*lity) + b2);
				cr->line_to(a1*(*litx) + b1, a2*y_min[idx_to_fill] + b2);
				cr->close_path();
				cr->fill();
				lastXvalue = *litx;
				lastYvalue = *lity;
			}
			++lity;
		}
		
		cr->stroke();


		///Affichage des valeurs en abscisse et ordonnée : a paufiner
		//draw text
		//Glib::RefPtr<Gdk::GC> gcText;
		//gcText = Gdk::GC::create(window);
		//std::ostringstream minAbs, maxAbs, minOrd, maxOrd;
		//minAbs << x_min;
		//maxAbs << x_max;
		//minOrd << y_min[0];
		//maxOrd << y_max[0];
		//window->draw_layout(gcText, 0.05*allocation.get_width(), 0.95*allocation.get_height(), create_pango_layout(minAbs.str()));
		//window->draw_layout(gcText, 0.9*allocation.get_width(), 0.95*allocation.get_height(), create_pango_layout(maxAbs.str()));
		//window->draw_layout(gcText, 0, 0.05*allocation.get_height(), create_pango_layout(maxOrd.str()));
		//window->draw_layout(gcText, 0, 0.95*allocation.get_height(), create_pango_layout(minOrd.str()));

	}
	return true;
}

///@brief Set X values range
///@param minX minimum value for X
///@param maxX maximum value for X
void XYPlotArea::SetXRange(double minX, double maxX)
{
	x_min = minX;
	x_max = maxX;
}

///@brief Set Y values range
///@param minY minimum value for Y
///@param maxY maximum value for Y
///@param idx Index of the plot to set
void XYPlotArea::SetYRange(double minY, double maxY, unsigned int idx)
{
	assert(idx < nbPlot);
	y_min[idx] = minY;
	y_max[idx] = maxY;
}

///@brief Draw the Axis	on a given Cairo context
///@param cr Cairo context
void XYPlotArea::DrawAxis(Cairo::RefPtr<Cairo::Context> cr)		
{
	cr->set_line_width(0.001);
	cr->set_source_rgb(0., 0., 0.);
	cr->move_to(0.05, 0.05);
	cr->line_to(0.05, 0.95);
	cr->line_to(0.95, 0.95);
	cr->stroke();
}

///@brief Clean the X list
void XYPlotArea::ClearXList()
{
	xList.clear();
}

///@brief Clean a given Y list
///@param idx Index of the plot to clear
void XYPlotArea::ClearYList(unsigned int idx)
{
	assert(idx < nbPlot);
	yList[idx].clear();
}	
